package classfile

import "fmt"
import "bytes"

type ConstantUtf8Info struct {
	str string
}

func (self *ConstantUtf8Info) readInfo(reader *ClassReader) {
	length := uint32(reader.readUint16())
	bytes := reader.readBytes(length)
	self.str = decodeMUTF8(bytes)
}

func decodeMUTF8(bytes []byte) string {
	result, err := mutf8ToString(bytes)
	if err != nil {
		panic("MUTF8 convert to UTF8 failed")
	}
	return result
}

// mutf8ToUtf8 converts MUTF-8 encoded bytes to standard UTF-8 encoded bytes.
func mutf8ToUtf8(mutf8 []byte) ([]byte, error) {
	var utf8 bytes.Buffer
	for len(mutf8) > 0 {
		b := mutf8[0]
		mutf8 = mutf8[1:]
		switch {
		case b <= 0x7F: // ASCII range, same in both UTF-8 and MUTF-8
			utf8.WriteByte(b)
		case b == 0xC0: // MUTF-8 special case for U+0000
			utf8.WriteByte(0x00)
		case b >= 0xC2 && b <= 0xDF: // Start of 2-byte sequence in UTF-8 (and MUTF-8)
			if len(mutf8) < 1 {
				return nil, fmt.Errorf("unexpected end of MUTF-8 sequence")
			}
			utf8.WriteByte(b)
			utf8.WriteByte(mutf8[0] ^ 0x80) // Remove the extra 0x80 added in MUTF-8 for high bytes
			mutf8 = mutf8[1:]
		case b >= 0xE0 && b <= 0xED: // Start of 3-byte sequence in UTF-8 (and MUTF-8), but different handling for surrogates
			if len(mutf8) < 2 {
				return nil, fmt.Errorf("unexpected end of MUTF-8 sequence")
			}
			utf8.WriteByte(b)
			nextByte := mutf8[0] ^ 0x80 // Remove the extra 0x80
			secondByte := mutf8[1] ^ 0x80
			mutf8 = mutf8[2:]

			// Surrogate pair handling (U+D800 to U+DFFF) in MUTF-8 is encoded differently than in UTF-8
			if b == 0xED && nextByte >= 0xA0 { // High surrogate start
				if secondByte < 0xDC || secondByte > 0xDF {
					return nil, fmt.Errorf("invalid high surrogate in MUTF-8 sequence")
				}
				// Encode high surrogate as a single code point in the supplementary plane
				utf8.WriteByte(0xED)
				utf8.WriteByte(nextByte)
				utf8.WriteByte(secondByte)
			} else {
				utf8.WriteByte(nextByte)
				utf8.WriteByte(secondByte)
			}
		default:
			return nil, fmt.Errorf("invalid byte %x in MUTF-8 sequence", b)
		}
	}
	return utf8.Bytes(), nil
}

// mutf8ToString converts MUTF-8 encoded bytes to a Go string.
func mutf8ToString(mutf8 []byte) (string, error) {
	utf8Bytes, err := mutf8ToUtf8(mutf8)
	if err != nil {
		return "", err
	}
	return string(utf8Bytes), nil
}